﻿/*
 * 环形缓冲区 
 */
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Threading.Tasks;

namespace Lockstep.Network
{
    public class CircularBuffer : Stream
    {
        public int ChunkSize = 8192;

        private readonly Queue<byte[]> bufferQueue = new Queue<byte[]>();

        private readonly Queue<byte[]> bufferCache = new Queue<byte[]>();

        /// <summary>
        /// 从CircularBuffer中写入数据时，从该位置开始写入
        /// </summary>
        public int LastIndex { get; set; }

        /// <summary>
        /// 从CircularBuffer中读取数据时，从该位置开始读取
        /// </summary>
        public int FirstIndex { get; set; }

        /// <summary>
        /// 从Queue当中获取首位数据很方便，但是没法获取末位的数据，所以这里特意分配一块内存给末位的数据区块，方便操作
        /// </summary>
        private byte[] lastBuffer;

        public CircularBuffer()
        {
            this.AddLast();
        }

        public CircularBuffer(int chunkSize)
        {
            this.ChunkSize = chunkSize;
            this.AddLast();
        }

        /// <summary>
        /// bufferQueue中的有效数据块加起来有多长
        /// </summary>
        public override long Length
        {
            get {
                int c = 0;
                if (this.bufferQueue.Count == 0)
                {
                    c = 0;
                }
                else
                {
                    c = (this.bufferQueue.Count - 1) * ChunkSize + this.LastIndex - this.FirstIndex;
                }
                if (c < 0)
                {
                    Log.Error(string.Format("TBuffer count < 0: {0}, {1}, {2}", this.bufferQueue.Count, this.LastIndex, this.FirstIndex));
                }
                return c;
            }
        }

        /// <summary>
        /// 从bufferCache弹出首个元素，压入bufferQueue中
        /// </summary>
        public void AddLast()
        {
            byte[] buffer;
            if (this.bufferCache.Count > 0)
            {
                buffer = this.bufferCache.Dequeue();
            }
            else
            {
                buffer = new byte[ChunkSize];
            }
            this.bufferQueue.Enqueue(buffer);
            this.lastBuffer = buffer;
        }

        /// <summary>
        /// 回收数据块
        /// 从bufferQueue弹出首个元素，压入bufferCache中
        /// </summary>
        public void RemoveFirst()
        {
            this.bufferCache.Enqueue(bufferQueue.Dequeue());
        }

        /// <summary>
        /// bufferQueue的首个数据
        /// </summary>
        public byte[] First
        {
            get {
                if (this.bufferQueue.Count == 0)
                {
                    this.AddLast();
                }
                return this.bufferQueue.Peek();
            }
        }

        /// <summary>
        /// bufferQueue的最后一个数据
        /// </summary>
        public byte[] Last
        {
            get {
                if (this.bufferQueue.Count == 0)
                {
                    this.AddLast();
                }
                return this.lastBuffer;
            }
        }

        /// <summary>
        /// 从CircularBuffer读取到stream流中
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        public async Task ReadAsync(Stream stream)
        {
            long buffLength = this.Length;
            int sendSize = this.ChunkSize - this.FirstIndex;

            if (sendSize > buffLength)
            {
                sendSize = (int)buffLength;
            }

            await stream.WriteAsync(this.First, this.FirstIndex, sendSize);

            this.FirstIndex += sendSize;
            if (this.FirstIndex == this.ChunkSize)
            {
                this.FirstIndex = 0;
                this.RemoveFirst();
            }
        }

        /// <summary>
        /// 从stream流写到CircularBuffer中
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        public async Task<int> WriteAsync(Stream stream)
        {
            int size = this.ChunkSize - this.LastIndex;

            //Stream.ReadAsync ： 从当前流异步读取字节序列，并将流中的位置提升读取的字节数。
            int n = await stream.ReadAsync(this.Last, this.LastIndex, size);

            if (n == 0)
            {
                return 0;
            }

            this.LastIndex += n;

            if (this.LastIndex == this.ChunkSize)//数据区块存储满了，则再添加一个数据区块到bufferQueue中，且重置LastIndex
            {
                this.AddLast();
                this.LastIndex = 0;
            }

            return n;
        }

        /// <summary>
        /// 接收到网络消息时，从Circularbuffer中取数据，从bufferQueue的队首中拷贝数据到buffer
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="offset">从buffer的offset偏移开始存数据到buffer中</param>
        /// <param name="count">需要读取多少位数据</param>
        /// <returns>读取出来多少字节的数据</returns>
        public override int Read(byte[] buffer, int offset, int count)
        {
            if (buffer.Length < offset + count)
            {
                throw new Exception($"bufferList length < coutn, buffer length: {buffer.Length} {offset} {count}");
            }

            long length = this.Length;
            if (length < count)//没有足够的数据，则减少取数据的量
            {
                count = (int)length;
            }

            int alreadyCopyCount = 0;
            while (alreadyCopyCount < count)
            {
                int n = count - alreadyCopyCount;//剩下需要拷贝的字节数
                if (ChunkSize - this.FirstIndex > n)//数据块中有足够的数据取出
                {
                    Array.Copy(this.First, this.FirstIndex, buffer, alreadyCopyCount + offset, n);
                    this.FirstIndex += n;
                    alreadyCopyCount += n;
                }
                else
                {
                    Array.Copy(this.First, this.FirstIndex, buffer, alreadyCopyCount + offset, ChunkSize - this.FirstIndex);//把能取的取出来
                    alreadyCopyCount += ChunkSize - this.FirstIndex;
                    this.FirstIndex = 0;//重置读取位置
                    this.RemoveFirst();//把队首的区块放到缓存里
                }
            }

            return count;
        }

        public void Write(byte[] buffer)
        {
            int alreadyCopyCount = 0;
            while (alreadyCopyCount < buffer.Length)
            {
                if (this.LastIndex == ChunkSize)
                {
                    this.AddLast();
                    this.LastIndex = 0;
                }

                int n = buffer.Length - alreadyCopyCount;
                if (ChunkSize - this.LastIndex > n)
                {
                    Array.Copy(buffer, alreadyCopyCount, this.lastBuffer, this.LastIndex, n);
                    this.LastIndex += buffer.Length - alreadyCopyCount;
                    alreadyCopyCount += n;
                }
                else
                {
                    Array.Copy(buffer, alreadyCopyCount, this.lastBuffer, this.LastIndex, ChunkSize - this.LastIndex);
                    alreadyCopyCount += ChunkSize - this.LastIndex;
                    this.LastIndex = ChunkSize;
                }
            }
        }

        /// <summary>
        /// 发送网络消息时，从buffer中压数据到CircularBuffer，从buffer拷贝数据bufferQueue的队尾
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="offset">从buffer中的offset偏移开始读取数据</param>
        /// <param name="count">需要拷贝的位数</param>
        public override void Write(byte[] buffer, int offset, int count)
        {
            int alreadyCopyCount = 0;
            while (alreadyCopyCount < count)//还有数据需要拷贝
            {
                if (this.LastIndex == ChunkSize)//数据块满了，则扩容
                {
                    this.AddLast();
                    this.LastIndex = 0;
                }

                int n = count - alreadyCopyCount;//剩下需要拷贝的数据字节数
                if (ChunkSize - this.LastIndex > n)//数据块中有足够的位置去存储
                {
                    Array.Copy(buffer, alreadyCopyCount + offset, this.lastBuffer, this.LastIndex, n);
                    this.LastIndex += count - alreadyCopyCount;
                    alreadyCopyCount += n;
                }
                else
                {
                    Array.Copy(buffer, alreadyCopyCount + offset, this.lastBuffer, this.LastIndex, ChunkSize - this.LastIndex);//只存当前剩下的数据块的容量，剩下的留给下一个循环去处理
                    alreadyCopyCount += ChunkSize - this.LastIndex;
                    this.LastIndex = ChunkSize;
                }
            }
        }

        public override void Flush()
        {
            //throw new NotImplementedException();
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            return 0;
            //throw new NotImplementedException();
        }

        public override void SetLength(long value)
        {
            //throw new NotImplementedException();
        }

        public override bool CanRead
        {
            get {
                return true;
            }
        }

        public override bool CanSeek
        {
            get {
                return false;
            }
        }

        public override bool CanWrite
        {
            get {
                return true;
            }
        }

        public override long Position { get; set; }
    }
}